<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Trace Viewer</title>

  <!-- D3 Library -->
  <script src="https://d3js.org/d3.v7.min.js"></script>

  <style>
    /* 1) Make body & html fill the screen */
    html, body {
      height: 100%;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      background-color: #f3f4f6;
      font-family: Arial, sans-serif;
    }

    h2 {
      text-align: center;
      margin: 20px 0;
      color: #4f46e5;
    }

    /* 2) Main container with two columns: left (trace) & right (details) */
    .container {
      flex: 1;
      display: flex;
      width: 100%;
      padding: 20px;
      box-sizing: border-box;
      gap: 20px;
      align-items: stretch; /* ensure columns fill height */
    }

    /* 3) Left side: trace container */
    .trace-container {
      flex: 3;
      background: #fff;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
      display: flex;
      flex-direction: column;
      height: 100%;
      overflow: hidden; /* no hidden scroll here; the child .svg-container handles it */
    }

    /* 4) A sub-container for the SVG so we can scroll if it grows large */
    .svg-container {
      flex: 1; /* Takes remaining vertical space */
      overflow: auto; /* Make this scrollable if the chart is tall */
      position: relative;
      margin-top: 10px;
    }

    /* 5) SVG sized dynamically in JS (width & height) */
    svg {
      background-color: #f9fafb;
      border: 1px solid #ccc;
      border-radius: 8px;
    }
    rect {
      cursor: pointer;
      transition: fill 0.2s ease;
    }
    rect:hover {
      fill: #6366f1 !important;
    }

    /* 6) Right side: details panel */
    .details-panel {
      flex: 1;
      background: #fff;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
      height: 100%;
    }

    /* 7) Inputs & button styling */
    input, button {
      padding: 10px;
      font-size: 16px;
      margin: 10px 5px;
      border-radius: 5px;
      border: 1px solid #ccc;
    }
    button {
      background-color: #4f46e5;
      color: white;
      cursor: pointer;
      border: none;
      padding: 10px 15px;
      transition: background 0.2s;
    }
    button:hover {
      background-color: #4338ca;
    }
  </style>
</head>
<body>

  <h2>Trace Viewer</h2>

  <div class="container">
    <!-- LEFT: Trace Container -->
    <div class="trace-container">
      <div>
        <!-- Two inputs: one for traceId, one for resourceName -->
        <input type="text" id="traceIdInput" placeholder="Enter Trace ID" />
        <input type="text" id="resourceNameInput" placeholder="Enter Resource Name" />
        <button id="loadBtn">Load Data</button>
      </div>
      <div class="svg-container">
        <svg></svg>
      </div>
    </div>

    <!-- RIGHT: Always-visible Details Panel -->
    <div class="details-panel">
      <h3 id="spanName">Select a span</h3>
      <p><strong>Service:</strong> <span id="serviceName">-</span></p>
      <p><strong>Duration:</strong> <span id="duration">-</span></p>
      <p><strong>Start Time:</strong> <span id="timestamp">-</span></p>
      <p><strong>Tags:</strong></p>
      <ul id="tagsList"></ul>
    </div>
  </div>

  <script>
    // ---------------------------------------------------------
    // Global Variables & DOM References
    // ---------------------------------------------------------
    let traceData = [];
    const svg = d3.select("svg");
    const g = svg.append("g"); // Group for drawing rectangles & text

    // DOM references
    const traceIdInput = document.getElementById("traceIdInput");
    const resourceNameInput = document.getElementById("resourceNameInput");
    const loadBtn = document.getElementById("loadBtn");

    const spanNameEl = document.getElementById("spanName");
    const serviceNameEl = document.getElementById("serviceName");
    const durationEl = document.getElementById("duration");
    const timestampEl = document.getElementById("timestamp");
    const tagsListEl = document.getElementById("tagsList");

    // ---------------------------------------------------------
    // Initialization
    // ---------------------------------------------------------
    document.addEventListener("DOMContentLoaded", init);

    function init() {
      loadBtn.addEventListener("click", fetchData);
    }

    // ---------------------------------------------------------
    // Fetch Data (Trace ID or Resource Name)
    // ---------------------------------------------------------
    async function fetchData() {
      const traceId = traceIdInput.value.trim();
      const resourceName = resourceNameInput.value.trim();

      // Validate user input
      if (traceId && resourceName) {
        alert("Please enter EITHER a Trace ID OR a Resource Name, not both.");
        return;
      }
      if (!traceId && !resourceName) {
        alert("Please enter a Trace ID or Resource Name.");
        return;
      }

      // Build the correct query param
      let url = "http://localhost:9009/local/debug/trace?";
      if (traceId) {
        url += `trace_id=${encodeURIComponent(traceId)}`;
      } else {
        url += `resource_name=${encodeURIComponent(resourceName)}`;
      }

      try {
        const response = await fetch(url);
        if (response.status === 404) {
          alert("No data found for the given Trace ID or Resource Name.");
          return;
        }
        if (!response.ok) {
          throw new Error(`HTTP error ${response.status}`);
        }
        traceData = await response.json();
        renderTrace();
      } catch (err) {
        alert("Failed to load data: " + err.message);
        console.error(err);
      }
    }

    // ---------------------------------------------------------
    // Render the Trace Visualization
    // ---------------------------------------------------------
    function renderTrace() {
      // Clear any existing shapes
      g.selectAll("*").remove();

      // If no data, do nothing
      if (!traceData || traceData.length === 0) return;

      // Determine min & max times
      const minTime = Math.min(...traceData.map(span => span.timestamp));
      const maxTime = Math.max(...traceData.map(span => span.timestamp + span.duration));

      // We'll create enough height for each span row
      const barSpacing = 30;
      const chartHeight = barSpacing * traceData.length + 50;

      // Get the container width
      const containerWidth = document.querySelector(".svg-container").clientWidth;

      // Dynamically set the SVG's width & height
      svg.attr("width", containerWidth).attr("height", chartHeight);

      // Create a linear scale for x-axis
      const xScale = d3.scaleLinear()
        .domain([minTime, maxTime])
        .range([50, containerWidth - 50]);

      // Sort spans by start time
      traceData.sort((a, b) => a.timestamp - b.timestamp);

      // Draw each span
      traceData.forEach((span, index) => {
        // Calculate bar width, clamp to a min so it's visible
        const rawWidth = xScale(span.timestamp + span.duration) - xScale(span.timestamp);
        const barWidth = Math.max(rawWidth, 5);

        // Rectangle
        g.append("rect")
          .attr("x", xScale(span.timestamp))
          .attr("y", 30 + index * barSpacing)
          .attr("width", barWidth)
          .attr("height", 20)
          .attr("fill", span.parentId ? "#6b7280" : "#8b5cf6")
          .on("click", () => showDetails(span));

        // Span label (black text so it's visible on small bars)
        g.append("text")
          .attr("x", xScale(span.timestamp) + 5)
          .attr("y", 45 + index * barSpacing)
          .attr("fill", "#000")
          .attr("font-size", "10px")
          .text(span.name);
      });
    }

    // ---------------------------------------------------------
    // Show Span Details in Right Panel
    // ---------------------------------------------------------
    function showDetails(span) {
      // Convert microseconds to seconds
      const durationInSeconds = (span.duration / 1_000_000).toFixed(6);

      // Convert timestamp (µs) to a human-readable date/time
      const date = new Date(span.timestamp / 1_000);
      const readableTime = date.toLocaleString();

      // Update DOM
      spanNameEl.innerText = span.name;
      serviceNameEl.innerText = span.localEndpoint?.serviceName || "-";
      durationEl.innerText = durationInSeconds + " s";
      timestampEl.innerText = readableTime;

      // Populate tags
      tagsListEl.innerHTML = "";
      if (span.tags) {
        Object.entries(span.tags).forEach(([key, value]) => {
          const li = document.createElement("li");
          li.innerHTML = `<strong>${key}:</strong> ${value}`;
          tagsListEl.appendChild(li);
        });
      }
    }
  </script>
</body>
</html>
